﻿namespace TNet
{
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;

    public class TcpLobbyServer : LobbyServer
    {
        public int instantUpdatesClientLimit = 50;
        private TNet.Buffer mBuffer;
        private bool mInstantUpdates = true;
        private long mLastChange;
        private ServerList mList = new ServerList();
        private TcpListener mListener;
        private int mPort;
        private List<TcpProtocol> mTcp = new List<TcpProtocol>();
        private Thread mThread;
        private long mTime;

        public override void AddServer(string name, int playerCount, IPEndPoint internalAddress, IPEndPoint externalAddress)
        {
            this.mList.Add(name, playerCount, internalAddress, externalAddress, this.mTime);
            this.mLastChange = this.mTime;
        }

        private BinaryWriter BeginSend(Packet type)
        {
            this.mBuffer = TNet.Buffer.Create();
            return this.mBuffer.BeginPacket(type);
        }

        private void EndSend(TcpProtocol tc)
        {
            this.mBuffer.EndPacket();
            tc.SendTcpPacket(this.mBuffer);
            this.mBuffer.Recycle();
            this.mBuffer = null;
        }

        private bool ProcessPacket(TNet.Buffer buffer, TcpProtocol tc)
        {
            if (tc.stage == TcpProtocol.Stage.Verifying)
            {
                return tc.VerifyRequestID(buffer, false);
            }
            BinaryReader reader = buffer.BeginReading();
            Packet packet2 = (Packet) reader.ReadByte();
            switch (packet2)
            {
                case Packet.Error:
                    return false;

                case Packet.Disconnect:
                    this.RemoveServer(tc);
                    this.mTcp.Remove(tc);
                    return true;

                case Packet.RequestPing:
                    this.BeginSend(Packet.ResponsePing);
                    this.EndSend(tc);
                    break;

                default:
                    switch (packet2)
                    {
                        case Packet.RequestSaveFile:
                        {
                            string fileName = reader.ReadString();
                            byte[] data = reader.ReadBytes(reader.ReadInt32());
                            base.SaveFile(fileName, data);
                            goto Label_0200;
                        }
                        case Packet.RequestLoadFile:
                        {
                            string str2 = reader.ReadString();
                            byte[] buffer3 = base.LoadFile(str2);
                            BinaryWriter writer = this.BeginSend(Packet.ResponseLoadFile);
                            writer.Write(str2);
                            if (buffer3 == null)
                            {
                                writer.Write(0);
                                break;
                            }
                            writer.Write(buffer3.Length);
                            writer.Write(buffer3);
                            break;
                        }
                        case Packet.RequestDeleteFile:
                            base.DeleteFile(reader.ReadString());
                            goto Label_0200;

                        default:
                            switch (packet2)
                            {
                                case Packet.RequestAddServer:
                                    if (reader.ReadUInt16() == 1)
                                    {
                                        ServerList.Entry newEntry = new ServerList.Entry();
                                        newEntry.ReadFrom(reader);
                                        if (newEntry.externalAddress.Address.Equals(IPAddress.None))
                                        {
                                            newEntry.externalAddress = tc.tcpEndPoint;
                                        }
                                        this.mList.Add(newEntry, this.mTime).data = tc;
                                        this.mLastChange = this.mTime;
                                        return true;
                                    }
                                    return false;

                                case Packet.RequestRemoveServer:
                                    if (reader.ReadUInt16() == 1)
                                    {
                                        IPEndPoint point;
                                        IPEndPoint tcpEndPoint;
                                        Tools.Serialize(reader, out point);
                                        Tools.Serialize(reader, out tcpEndPoint);
                                        if (tcpEndPoint.Address.Equals(IPAddress.None))
                                        {
                                            tcpEndPoint = tc.tcpEndPoint;
                                        }
                                        this.RemoveServer(point, tcpEndPoint);
                                        return true;
                                    }
                                    return false;

                                case Packet.RequestServerList:
                                    if (reader.ReadUInt16() == 1)
                                    {
                                        tc.data = 0L;
                                        return true;
                                    }
                                    return false;
                            }
                            goto Label_0200;
                    }
                    this.EndSend(tc);
                    break;
            }
        Label_0200:
            return false;
        }

        private bool RemoveServer(Player player)
        {
            bool flag = false;
            List<ServerList.Entry> list = this.mList.list;
            lock (list)
            {
                int size = this.mList.list.size;
                while (size > 0)
                {
                    ServerList.Entry entry = this.mList.list[--size];
                    if (entry.data == player)
                    {
                        this.mList.list.RemoveAt(size);
                        this.mLastChange = this.mTime;
                        flag = true;
                    }
                }
            }
            return flag;
        }

        public override void RemoveServer(IPEndPoint internalAddress, IPEndPoint externalAddress)
        {
            if (this.mList.Remove(internalAddress, externalAddress))
            {
                this.mLastChange = this.mTime;
            }
        }

        public override bool Start(int listenPort)
        {
            this.Stop();
            try
            {
                this.mListener = new TcpListener(IPAddress.Any, listenPort);
                this.mListener.Start(50);
                this.mPort = listenPort;
            }
            catch (Exception)
            {
                return false;
            }
            this.mThread = new Thread(new ThreadStart(this.ThreadFunction));
            this.mThread.Start();
            return true;
        }

        public override void Stop()
        {
            if (this.mThread != null)
            {
                this.mThread.Abort();
                this.mThread = null;
            }
            if (this.mListener != null)
            {
                this.mListener.Stop();
                this.mListener = null;
            }
            this.mList.Clear();
        }

        private void ThreadFunction()
        {
            while (true)
            {
                this.mTime = DateTime.UtcNow.Ticks / 0x2710L;
                while ((this.mListener != null) && this.mListener.Pending())
                {
                    TcpProtocol item = new TcpProtocol();
                    item.StartReceiving(this.mListener.AcceptSocket());
                    this.mTcp.Add(item);
                }
                TNet.Buffer buffer = null;
                for (int i = 0; i < this.mTcp.size; i++)
                {
                    TcpProtocol tc = this.mTcp[i];
                    while (tc.ReceivePacket(out buffer))
                    {
                        try
                        {
                            if (!this.ProcessPacket(buffer, tc))
                            {
                                this.RemoveServer(tc);
                                tc.Disconnect();
                            }
                        }
                        catch (Exception)
                        {
                            this.RemoveServer(tc);
                            tc.Disconnect();
                        }
                        if (buffer != null)
                        {
                            buffer.Recycle();
                            buffer = null;
                        }
                    }
                }
                if (this.mTcp.size > this.instantUpdatesClientLimit)
                {
                    this.mInstantUpdates = false;
                }
                for (int j = 0; j < this.mTcp.size; j++)
                {
                    TcpProtocol protocol3 = this.mTcp[j];
                    if (((protocol3.stage == TcpProtocol.Stage.Connected) && (protocol3.data != null)) && (protocol3.data is long))
                    {
                        long data = (long) protocol3.data;
                        if ((data == 0) || ((data < this.mLastChange) && (this.mInstantUpdates || ((data + 0xfa0L) <= this.mTime))))
                        {
                            if (buffer == null)
                            {
                                buffer = TNet.Buffer.Create();
                                BinaryWriter writer = buffer.BeginPacket(Packet.ResponseServerList);
                                this.mList.WriteTo(writer);
                                buffer.EndPacket();
                            }
                            protocol3.SendTcpPacket(buffer);
                            protocol3.data = this.mTime;
                        }
                    }
                }
                if (buffer != null)
                {
                    buffer.Recycle();
                    buffer = null;
                }
                Thread.Sleep(1);
            }
        }

        public override bool isActive
        {
            get
            {
                return (this.mListener != null);
            }
        }

        public override int port
        {
            get
            {
                return this.mPort;
            }
        }
    }
}

